/**************************************************************************
*   Copyleft 2009 by        											  *
*	Iwanicki Krzysztof													  *
*   <www.gamma.mini.pw.edu.pl/~iwanickik>		                          *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
***************************************************************************/
#ifndef __VMEMORY_TRACKER_H__
#define __VMEMORY_TRACKER_H__

#include <iostream>
#include <ostream>
#include <fstream>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <new>
#include <Types/vTypes.h>

#ifdef V_DO_MEMORY_TRACKING

namespace Vendetta {
#define V_MEMORY_LEAKS_FILE		"MemoryLeaks.log"
	/*! \class vMemoryTracker.
	 *	\brief Manages memory allocations / deallocations in debug mode.
	 *
	 *	In debug mode all C++ like memory operators (new, delete and delete[])
	 *	are overloaded with custom operators.
	 *
	 *	Current implementation is pretty slow, since it all works on
	 *	linear list, but to my own surprise in real (virtual:)) life
	 *	situations, its speed is of fully no importance.
	 */
	class vMemoryTracker {

	public:

		/*! \brief Default constructor.
		 */
		vMemoryTracker();

		/*! \brief Default destructor.
		 */
		~vMemoryTracker();

		/*! \brief Allocates required amount of memory.
		 *
		 *	\param size Bytes count.
		 *	\param file File name this allocation took place in.
		 *	\param line Line number in file this allocation took place in.
		 *	\return Pointer to newly allocated memory.
		 */
		vVoid*	Alloc(const vUInt size, const vChar* file, const vInt line);

		/*! \brief Frees memory assigned to given pointer.
		 *
		 *	\param ptr Pointer to be freed.
		 */
		vVoid	Free(vVoid* ptr);

		/*! \brief Returns instance of Memory tracker.
		*
		*/
		inline static vMemoryTracker& Instance() {
			static vMemoryTracker pMemTracker;

			return pMemTracker;
		}

	private:

		/*! \brief Adds new allocation info to custom container.
		 *
		 *	After this has been done an allocated memory is "waiting" for
		 *	being deallocated - any memory block that wasn't deallocated is
		 *	considered to be a memory leak - something we don't really like.
		 *
		 *	\param address Address of start of allocated memory block.
		 *	\param size Size of allocated memory.
		 *	\param file File of allocation.
		 *	\param line Line in file.
		 */
		vVoid	add(const vUInt address, const vUInt size, const vChar* file, const vInt line);

		/*! \brief Removes allocation info from custom container.
		 *
		 *	\param address address of allocation (key to search in custom container)
		 */
		vBool	remove(const vUInt address);

		/*! \brief Dump info to "memory_leaks.log" file.
		*
		*/
		vVoid dumpInfo();

		// Every single call to new() gets its new tAlloc structure
		struct tAlloc {
			vUInt	Address;
			vUInt	Size;
			vChar	File[512];
			vInt	Line;
			tAlloc*	Next;
		};

	private:
		vUInt		mAllocated;		//! Total allocated bytes count during engine's work
		vUInt		mFreed;			//! Total freed bytes count during engine's work

		tAlloc*		mpAllocs;		//! Pointer to list head
		vInt		mNumAllocs;		//! Length of list above
		tAlloc*		mpFreeAllocs;	//! Pointer to list of free elements
	};
}
/*! \brief Overloaded C++ style operators.
 *
 *	Note that we can't use new nor delete inside memory tracking code!
 *	Instead malloc() and free() are used.
 */
inline void*	operator new	 (unsigned int size)									{ return Vendetta::vMemoryTracker::Instance().Alloc(size, NULL, -1);	}
inline void*	operator new[]	 (unsigned int size)									{ return Vendetta::vMemoryTracker::Instance().Alloc(size, NULL, -1);	}
inline void*	operator new	 (unsigned int size, const char* file, const int line)	{ return Vendetta::vMemoryTracker::Instance().Alloc(size, file, line);}
inline void*	operator new[]	 (unsigned int size, const char* file, const int line)	{ return Vendetta::vMemoryTracker::Instance().Alloc(size, file, line);}
inline void		operator delete	 (void* ptr)											{ Vendetta::vMemoryTracker::Instance().Free(ptr);	}
inline void		operator delete[](void* ptr)											{ Vendetta::vMemoryTracker::Instance().Free(ptr); }
inline void		operator delete	 (void* ptr, const char* file, const int line)			{ Vendetta::vMemoryTracker::Instance().Free(ptr); }
inline void		operator delete[](void* ptr, const char* file, const int line)			{ Vendetta::vMemoryTracker::Instance().Free(ptr); }
		
#define new		new(__FILE__, __LINE__)

#endif

#endif //__VMEMORY_TRACKER_H__
